export const parse = (
  stream,
  schema,
  result = {},
  parent = result,
  events = {}
) => {
  const parseMain = () => {
    if (events.terminated) {
      return result
    }

    if (Array.isArray(schema)) {
      let promiseLoop = Promise.resolve()
      for (let i = 0; i < schema.length; i++) {
        let partSchema = schema[i]
        promiseLoop = promiseLoop.then(() =>
          parse(stream, partSchema, result, parent, events)
        )
      }
      return promiseLoop
    }
    if (typeof schema === 'function') {
      // read
      return schema(stream, result, parent, parse, events)
    }
    const key = Object.keys(schema)[0]
    if (Array.isArray(schema[key])) {
      parent[key] = {}
      return parse(stream, schema[key], result, parent[key], events)
    } else {
      //read
      return Promise.resolve(schema[key](stream, result, parent, parse, events)).then(
        (v) => (parent[key] = v)
      )
    }
  }

  return Promise.resolve()
    .then(() => {
      if (events.parseBefore) {
        return events.parseBefore(schema, result, parent, events)
      }
    })
    .then(parseMain)
    .then(() => {
      if (events.parseAfter) {
        return events.parseAfter(schema, result, parent, events)
      }
    })
    .then(() => result)
}

export const conditional = (schema, conditionFunc) => (
  stream,
  result,
  parent,
  parse,
  events
) => {
  return Promise.resolve(conditionFunc(stream, result, parent, events)).then(
    (ok) => {
      if (ok) {
        return parse(stream, schema, result, parent, events)
      }
    }
  )
}

export const loop = (schema, continueFunc) => (
  stream,
  result,
  parent,
  parse,
  events
) => {
  const arr = []
  const next = () => {
    return Promise.resolve(continueFunc(stream, result, parent, events)).then(
      (hasNext) => {
        if (hasNext && !events.terminated) {
          const newParent = {}
          return parse(stream, schema, result, newParent, events)
            .then(() => arr.push(newParent))
            .then(next)
        } else {
          return arr
        }
      }
    )
  }
  return next()
}
